package db;

import ptolemy.kernel.ComponentEntity;
import ptolemy.actor.*;
import ptolemy.actor.parameters.ParameterPort;

import java.util.*;
import java.io.*;
import java.net.URL;

import antlr.*;
import antlr.collections.AST;

import util.*;
import JavaGrammar.*;
import SymbolTable.*;
import MoC.ClassResult;

//////////////////////////////////////////////////////////////////////////
//// ClassInfo
/**

     Entrada da database de classes.

@author Ivan Jeukens
@version v 0.1 03/10/2001
*/
public class ClassInfo extends dbInfo {

    public ClassInfo(DataBase classDataDB, ComponentEntity actor, Class c) {
        _classDataDB = classDataDB;
        _classes = new LinkedList();

        _class = c;
        _result = new ClassResult(this);
        
        _usedBy = new LinkedList();

        _build(actor);
        _buildPortPar(actor);
    }

    ///////////////////////////////////////////////////////////////////
    ////                        public methods                     ////

    /*
    public void addClass(ClassInfo info) {
        _classes.add(info);
    }

    public boolean containsClass(ClassInfo info) {
        return _classes.contains(info);
    }

    public LinkedList getClasses() {
        return _classes;
    }

    public ClassInfo getClass(int index) {
        if(index < 0 || index > _classes.size()) return null;
        return (ClassInfo) _classes.get(index);
    }    
*/

    public ClassData getRootClass() {
        return _rootClass;
    }

    public Iterator classesIterator() {
        return _classes.iterator();
    }

    // Dado uma lista de coletores, visita todas a classinfo desse ator
    public void visitClasses(LinkedList collectors) {
        for(int i = 0;i < _classes.size();i++) {
            /*
            ClassInfo cinfo = (ClassInfo) _classes.get(i);
            if(cinfo.getName().equals("TypedAtomicActor") ||
               cinfo.getName().equals("ptolemy.actor.TypedAtomicActor") ||
               cinfo.getName().equals("AtomicActor") ||
               cinfo.getName().equals("ptolemy.actor.AtomicActor")) break;
            cinfo.visit(collectors);
            */
        }
    }

    public void debug(String basepath) {
        try {
            File f = new File(basepath + "/" + _name + "/");
            f.mkdirs();

            String name = new String(basepath + "/" + _name + "/");

            File cfile = new File(name + "/classes");
            cfile.createNewFile();
            FileOutputStream fo = new FileOutputStream(cfile);
            DataOutputStream ps = new DataOutputStream(fo);

            for(int i = 0;i < _classes.size();i++) {
                ClassData cdata = (ClassData) _classes.get(i);
                ps.writeUTF(cdata.getName());
                cdata.debug(name);
                // cdata.checkCode();
            }
            fo.close();
        }
        catch(IOException ex) {
            System.out.println(ex);
        }
    }

    public Class getActorClass() {
        return _class;
    }

    public ClassResult getResult() {
        return _result;
    }

    public void buildUsedBy(HashMap actorClassMap, DataBase actorDB) {
        LinkedList atomicActors = (LinkedList) actorClassMap.get(_class.getName());
        for(int i = 0;i < atomicActors.size();i++) {
            ComponentEntity e = (ComponentEntity) atomicActors.get(i);
            AtomicActorInfo ainfo = (AtomicActorInfo) actorDB.get(e.getFullName());
            _usedBy.add(ainfo);
        }
    }

    public Iterator usedBy() {
        return _usedBy.iterator();
    }
    
    public LinkedList getUsedBy() {
        return _usedBy;
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    ////

    // se os portos forem adicionados depois, como no caso do ator Expression
    // nao vai funcionar aqui.
    private void _buildPortPar(ComponentEntity actor) {
        // Completa a informacao das symbol table de portos, determinando
        // a direcao de cada um.
        SymbolTable ports = _rootClass.getPorts();
        Iterator ite = actor.portList().iterator();
        while(ite.hasNext()) {
            IOPort p = (IOPort) ite.next();
            if(p instanceof ParameterPort) continue;

            STPortEntry pe = (STPortEntry) ports.find(p.getName());
            if(pe == null) {
                System.out.println("\nNao achei o porto em ACTOR " + 
                    actor.getFullName() + " porto " + p.getName() + " em:");
                System.out.println(ports);
            }

            if(p.isInput()) pe.setInput(true);
            if(p.isOutput()) pe.setOutput(true);
        }
    }

    private void _build(ComponentEntity actor) {
        LinkedList classAST = new LinkedList();
        LinkedList className = new LinkedList();
        LinkedList packNames = new LinkedList();
        NameCollector nc;

        ClassData superclassInDB = null;
        Class sup = actor.getClass();
        String fname;

        do {
            // faz o parse de um arquivo
            Class c = sup;
            AST root = Util.parseFile(Util.absolutePath(c.getName()));
            classAST.add(root);

            // determina o nome completo da classe, incluindo o pacote
            boolean haspkg = false;
            String pack = null;
            AST v = root;
            StringBuffer cname = new StringBuffer();
            if(v.getType() == JavaTokenTypes.PACKAGE_DEF) {
                nc = new NameCollector();
                Util.inorderTraverse(v.getFirstChild(), nc);
                cname.append(nc.getName());
                pack = nc.getName();
                haspkg = true;
            }
            // salva o nome do pacote
            packNames.add(pack);

            v = root;
            int type = v.getType();
            while(v != null && type != JavaTokenTypes.CLASS_DEF) {
                v = v.getNextSibling();
                type = v.getType();
            }
            v = v.getFirstChild();
            v = v.getNextSibling();

            if(haspkg) {
                cname.append("." + v.getText());
            }
            else {
                cname.append(v.getText());
            }

            // Determina se essa classe do ator, e consequentemente suas
            // subclasses, esta na data base.
            if(_classDataDB.contains(cname.toString())) {
                ClassData ctmp = (ClassData) _classDataDB.getInfo(
                     cname.toString());

                superclassInDB = ctmp;
                if(className.size() == 0) {
                    _name = cname.toString();
                    _rootClass = ctmp;
                }
                break;
            }

            className.add(cname.toString());
            if(className.size() == 1) {
                _name = cname.toString();
            }

            // Determina que a classe extende ...
            v = v.getNextSibling();
            nc = new NameCollector();
            if(v.getFirstChild() == null) {
                // e Object, pode sair fora.
                break;
            }
            else {
                Util.inorderTraverse(v.getFirstChild(), nc);
            }

            // Carrega a classe para determinar sua subclasse
            ClassLoader cl = ClassLoader.getSystemClassLoader();
            c = null;
            try {
                boolean b = Compiler.compileClasses(cname.toString());
                c = cl.loadClass(cname.toString());
                b  = Compiler.compileClass(c);
            } catch(ClassNotFoundException ex) {
                System.out.println(ex);
                System.out.println("Classname: " + cname.toString());
            }
            if(c == null) return;

            sup = c.getSuperclass();
            //fname = Util.absolutePath(sup.getName());
        } 
        while(!(nc.getName().equals("TypedAtomicActor") ||
               nc.getName().equals("AtomicActor")));
        //while(sup != Object.class);

        // Para as novas classes, cria um classinfo e introduz na lista
        // deste ator.
        DataBase db = _classDataDB;
        ClassData superclass = null;
        for(int i = 0;i < className.size();i++) {
            String cname = (String) className.get(i);
            ClassData cinfo;

            AST root = (AST) classAST.get(i);

            cinfo = new ClassData(cname, root, (String) packNames.get(i), false);
            if(superclass != null) superclass.setSuperClass(cinfo);

            superclass = cinfo;

            db.add(cinfo);
            _classes.add(cinfo);

            if(i == 0) {
                _rootClass = cinfo;
            }
        }

        _newClassIndex = _classes.size();
        // Adiciona as classes que ja existiam na DB na lista deste ator
        // Para este codigo funcionar, tenho que garantir que a cadeia de
        // hierarquia nao foi alterada.
        if(superclassInDB != null) {
            if(superclass != null) {
                superclass.setSuperClass(superclassInDB);
            }
            superclass = superclassInDB;
            while(superclass != null) {
                _classes.add(superclass);
                superclass = superclass.getSuperClass();
            }
        }
        else {
            superclass.setSuperClass(null);
        }

        String u = Util.absolutePath(_name);
        try {
            _url = new URL("file://" + u);
        }
        catch(Exception ex) {
            System.out.println(ex);
        }
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private variables                  ////

    private ClassData _rootClass;

    private DataBase _classDataDB;

     // as classes que usadas por esse ator atomico
    private LinkedList _classes;

    private int _newClassIndex;  // um indice para a lista _classes que indica
                                 // ate onde que indice as classes nao estavam 
                                 // na database

    private Class _class;
    private ClassResult _result;
    private LinkedList _usedBy;
}
